home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / WASTE 1.1a4 / WASTE Source / WELineLayout.p < prev    next >
Encoding:
Text File  |  1994-11-10  |  26.3 KB  |  965 lines  |  [TEXT/PJMM]

  1. unit WELineLayout;
  2.  
  3. { WASTE PROJECT: }
  4. { Line Layout, Creation & Destruction, Getting and Setting Variables, etc. }
  5.  
  6. { Copyright © 1993-1994 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WEDrawing;
  12.  
  13.     function WENew (var destRect, viewRect: LongRect;
  14.                                     flags: Integer;
  15.                                     var hWE: WEHandle): OSErr;
  16.     procedure WEDispose (hWE: WEHandle);
  17.     procedure WEStopInlineSession (hWE: WEHandle);
  18.     function _WERemoveLine (lineIndex: LongInt;
  19.                                     pWE: WEPtr): OSErr;
  20.     function _WERecalBreaks (var startLine, endLine: LongInt;
  21.                                     hWE: WEHandle): OSErr;
  22.     procedure _WERecalSlops (firstLine, lastLine: LongInt;
  23.                                     hWE: WEHandle);
  24.     function WECalText (hWE: WEHandle): OSErr;
  25.     function WEGetTextLength (hWE: WEHandle): LongInt;
  26.     function WECountLines (hWE: WEHandle): LongInt;
  27.     function WEGetHeight (startLine, endLine: LongInt;
  28.                                     hWE: WEHandle): LongInt;
  29.     procedure WEGetSelection (var selStart, selEnd: LongInt;
  30.                                     hWE: WEHandle);
  31.     procedure WEGetDestRect (var destRect: LongRect;
  32.                                     hWE: WEHandle);
  33.     procedure WESetDestRect (var destRect: LongRect;
  34.                                     hWE: WEHandle);
  35.     procedure WEGetViewRect (var viewRect: LongRect;
  36.                                     hWE: WEHandle);
  37.     procedure WESetViewRect (var viewRect: LongRect;
  38.                                     hWE: WEHandle);
  39.     function WEGetText (hWE: WEHandle): Handle;
  40.     function WEGetChar (offset: LongInt;
  41.                                     hWE: WEHandle): Char;
  42.     function WEGetAlignment (hWE: WEHandle): SignedByte;
  43.     function WEFeatureFlag (feature: Integer;
  44.                                     action: Integer;
  45.                                     hWE: WEHandle): Integer;
  46.     function WEUseText (text: Handle;
  47.                                     hWE: WEHandle): OSErr;
  48.     function WEGetInfo (selector: OSType;
  49.                                     info: Ptr;
  50.                                     hWE: WEHandle): OSErr;
  51.     function WESetInfo (selector: OSType;
  52.                                     info: Ptr;
  53.                                     hWE: WEHandle): OSErr;
  54.  
  55. implementation
  56.     uses
  57.         Drag, FixMath, QDOffscreen, TextServices;
  58.  
  59.     function _WERegisterWithTSM (hWE: WEHandle): OSErr;
  60.  
  61. { the WE record must be already locked }
  62.  
  63.         label
  64.             1;
  65.         var
  66.             pWE: WEPtr;
  67.             typeList: InterfaceTypeList;
  68.             err: OSErr;
  69.     begin
  70.         pWE := hWE^;
  71.  
  72. { do nothing if the Text Services Manager isn't available }
  73.         if BTST(pWE^.flags, weFHasTextServices) then
  74.             begin
  75.                 typeList[0] := kTextService;
  76.                 err := NewTSMDocument(1, typeList, pWE^.tsmReference, LongInt(hWE));
  77.                 if (err <> noErr) then
  78.  
  79. { we don't consider it an error if our client application isn't TSM-aware }
  80.                     if (err <> tsmNeverRegisteredErr) then
  81.                         goto 1;
  82.             end;
  83.  
  84. { clear result code }
  85.         err := noErr;
  86.  
  87. 1:
  88. { return result code }
  89.         _WERegisterWithTSM := err;
  90.  
  91.     end;  { _WERegisterWithTSM }
  92.  
  93.     procedure WEStopInlineSession (hWE: WEHandle);
  94.         var
  95.             tsmReference: TSMDocumentID;
  96.     begin
  97.         tsmReference := hWE^^.tsmReference;
  98.         if (tsmReference <> nil) then
  99.             IgnoreShort(FixTSMDocument(tsmReference));
  100.     end;  { WEStopInlineSession }
  101.  
  102.     function WENew (var destRect, viewRect: LongRect;
  103.                                     flags: Integer;
  104.                                     var hWE: WEHandle): OSErr;
  105.         label
  106.             1, 2;
  107.         var
  108.             pWE: WEPtr;
  109.             allocFlags: Integer;
  110.             weFlags: LongInt;
  111.             response: LongInt;
  112.             r: Rect;
  113.             err: OSErr;
  114.     begin
  115.         pWE := nil;
  116.         weFlags := flags;
  117.         allocFlags := kAllocClear;
  118.  
  119. { allocate the WE record }
  120.         err := _WEAllocate(SizeOf(WERec), allocFlags, hWE);
  121.         if (err <> noErr) then
  122.             goto 1;
  123.  
  124. { lock it down }
  125.         HLock(Handle(hWE));
  126.         pWE := hWE^;
  127.  
  128. { get active port }
  129.         GetPort(pWE^.port);
  130.  
  131. { determine whether temporary memory should be used for data structures }
  132.         if BTST(weFlags, weFUseTempMem) then
  133.             allocFlags := allocFlags + kAllocTemp;
  134.  
  135. { allocate the text handle (initially empty) }
  136.         err := _WEAllocate(0, allocFlags, pWE^.hText);
  137.         if (err <> noErr) then
  138.             goto 1;
  139.  
  140. { allocate the line array }
  141.         err := _WEAllocate(2 * SizeOf(LineRec), allocFlags, pWE^.hLines);
  142.         if (err <> noErr) then
  143.             goto 1;
  144.  
  145. { allocate the style table }
  146.         err := _WEAllocate(SizeOf(StyleTableElement), allocFlags, pWE^.hStyles);
  147.         if (err <> noErr) then
  148.             goto 1;
  149.  
  150. { allocate the run array }
  151.         err := _WEAllocate(2 * SizeOf(RunArrayElement), allocFlags, pWE^.hRuns);
  152.         if (err <> noErr) then
  153.             goto 1;
  154.  
  155. { check for the presence of various system software features }
  156. { determine whether Color QuickDraw is available }
  157.         if (%_Gestalt(gestaltQuickDrawVersion, response) = noErr) then
  158.             if (response >= gestalt8BitQD) then
  159.                 BSET(weFlags, weFHasColorQD);
  160.  
  161. { determine whether the Text Services manager is available }
  162.         if (%_Gestalt(gestaltTSMgrVersion, response) = noErr) then
  163.             BSET(weFlags, weFHasTextServices);
  164.  
  165. { determine if there are any non-Roman scripts enabled }
  166.         if (GetEnvirons(smEnabled) > 1) then
  167.             BSET(weFlags, weFNonRoman);
  168.  
  169. { determine whether a double-byte script is installed }
  170.         if (GetEnvirons(smDoubleByte) <> 0) then
  171.             BSET(weFlags, weFDoubleByte);
  172.  
  173. { determine whether the Drag Manager is available }
  174.         if (%_Gestalt(gestaltDragMgrAttr, response) = noErr) then
  175.             if BTST(response, gestaltDragMgrPresent) then
  176.                 BSET(weFlags, weFDragAndDrop);
  177.  
  178. { initialize miscellaneous fields of the WE record }
  179.         pWE^.nLines := 1;
  180.         pWE^.nStyles := 1;
  181.         pWE^.nRuns := 1;
  182.         pWE^.viewRect := viewRect;
  183.         pWE^.destRect := destRect;
  184.         pWE^.flags := weFlags;
  185.         pWE^.tsmAreaStart := kInvalidOffset;
  186.         pWE^.tsmAreaEnd := kInvalidOffset;
  187.         pWE^.dragCaretOffset := kInvalidOffset;
  188.  
  189. { create a region to hold the view rectangle }
  190.         pWE^.viewRgn := NewRgn;
  191.         WELongRectToRect(viewRect, r);
  192.         RectRgn(pWE^.viewRgn, r);
  193.  
  194. { initialize the style run array }
  195.         with pWE^.hRuns^^[1] do
  196.             begin
  197.                 runStart := 1;
  198.                 styleIndex := -1;
  199.             end;
  200.  
  201. { initialize the style table }
  202.         with pWE^.hStyles^^[0] do
  203.             begin
  204.                 refCount := 1;
  205.  
  206. { copy text attributes from the active graphics port }
  207.                 info.runStyle.tsFont := pWE^.port^.txFont;
  208.                 info.runStyle.tsSize := pWE^.port^.txSize;
  209.                 info.runStyle.tsFace := GrafPtr1(pWE^.port)^.txFace;
  210.                 if BTST(weFlags, weFHasColorQD) then
  211.                     GetForeColor(info.runStyle.tsColor);
  212.                 _WEFillFontInfo(pWE^.port, info);
  213.  
  214.             end;
  215.  
  216. { initialize the line array }
  217.         err := WECalText(hWE);
  218.         if (err <> noErr) then
  219.             goto 1;
  220.  
  221. { register with the Text Services Manager }
  222.         err := _WERegisterWithTSM(hWE);
  223.         if (err <> noErr) then
  224.             goto 1;
  225.  
  226. { unlock the WE record }
  227.         HUnlock(Handle(hWE));
  228.  
  229. { clear result code }
  230.         err := noErr;
  231.  
  232. { skip clean-up section }
  233.         goto 2;
  234.  
  235. 1:
  236. { clean up }
  237.         if (pWE <> nil) then
  238.             begin
  239.                 _WEForgetHandle(pWE^.hText);
  240.                 _WEForgetHandle(pWE^.hLines);
  241.                 _WEForgetHandle(pWE^.hStyles);
  242.                 _WEForgetHandle(pWE^.hRuns);
  243.                 if (pWE^.viewRgn <> nil) then
  244.                     DisposeRgn(pWE^.viewRgn);
  245.             end;
  246.         _WEForgetHandle(hWE);
  247.  
  248. 2:
  249. { return result code }
  250.         WENew := err;
  251.  
  252.     end;  { WENew }
  253.  
  254. (*** WEDispose needs to destroy the action stack (aka the Undo buffer), ***)
  255. (*** but unfortunately, WEClearUndo() is defined in a subsequent unit, ***)
  256. (*** so I've temporarily added this ugly external declaration ***)
  257.  
  258.     procedure WEClearUndo (hWE: WEHandle);
  259.     external;
  260.  
  261.     procedure WEDispose (hWE: WEHandle);
  262.         var
  263.             pWE: WEPtr;
  264.             pTable: StyleTablePtr;
  265.             index: LongInt;
  266.     begin
  267.  
  268. { sanity check: make sure WE isn't NIL }
  269.         if (hWE = nil) then
  270.             Exit(WEDispose);
  271.  
  272. { lock the WE record }
  273.         HLock(Handle(hWE));
  274.         pWE := hWE^;
  275.  
  276. { clear the Undo buffer }
  277.         WEClearUndo(hWE);
  278.  
  279. { unregister with the Text Services Manager }
  280.         if (pWE^.tsmReference <> nil) then
  281.             begin
  282.                 IgnoreShort(DeleteTSMDocument(pWE^.tsmReference));
  283.                 pWE^.tsmReference := nil;
  284.             end;
  285.  
  286. { dispose of the offscreen graphics world }
  287.         if (pWE^.offscreenPort <> nil) then
  288.             begin
  289.                 DisposeGWorld(GWorldPtr(pWE^.offscreenPort));
  290.                 pWE^.offscreenPort := nil;
  291.             end;
  292.  
  293.         if (pWE^.hStyles <> nil) then
  294.             begin
  295.  
  296. { lock the style table }
  297.                 HLock(Handle(pWE^.hStyles));
  298.                 pTable := pWE^.hStyles^;
  299.  
  300. { walk the style table, disposing of all embedded objects referenced there }
  301.                 index := 0;
  302.                 while (index < pWE^.nStyles) do
  303.                     with pTable^[index] do
  304.                         begin
  305.                             if (refCount > 0) then
  306.                                 if (_WEFreeObject(WEObjectDescHandle(info.runStyle.tsObject)) <> noErr) then
  307.                                     ;        { don't known what to do with errors }
  308.                             index := index + 1;
  309.                         end;
  310.             end;
  311.  
  312. { dispose of auxiliary data structures }
  313.         _WEForgetHandle(pWE^.hText);
  314.         _WEForgetHandle(pWE^.hLines);
  315.         _WEForgetHandle(pWE^.hStyles);
  316.         _WEForgetHandle(pWE^.hRuns);
  317.         DisposeRgn(pWE^.viewRgn);
  318.  
  319. { dispose of the WE record }
  320.         DisposeHandle(Handle(hWE));
  321.  
  322.     end;  { WEDispose }
  323.  
  324.     function _WERemoveLine (lineIndex: LongInt;
  325.                                     pWE: WEPtr): OSErr;
  326.  
  327. { remove the specified element from the line array }
  328.  
  329.     begin
  330.  
  331. { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  332.         _WERemoveLine := _WERemoveSlot(pWE^.hLines, lineIndex, SizeOf(LineRec));
  333.  
  334. { decrement line count }
  335.         pWE^.nLines := pWE^.nLines - 1;
  336.  
  337.     end;  { _WERemoveLine }
  338.  
  339.     function InsertLine (lineIndex: LongInt;
  340.                                     var theLine: LineRec;
  341.                                     pWE: WEPtr): OSErr;
  342.  
  343. { insert the specified element in the line array }
  344.  
  345.         var
  346.             err: OSErr;
  347.     begin
  348.         InsertLine := noErr;
  349.  
  350. { do the insertion }
  351.         err := _WEInsertSlot(pWE^.hLines, @theLine, lineIndex, SizeOf(theLine));
  352.         if (err <> noErr) then
  353.             begin
  354.                 InsertLine := err;
  355.                 Exit(InsertLine);
  356.             end;
  357.  
  358. { increment line count }
  359.         pWE^.nLines := pWE^.nLines + 1;
  360.  
  361.     end;  { InsertLine }
  362.  
  363.     procedure _WEBumpOrigin (lineIndex: LongInt;
  364.                                     deltaOrigin: LongInt;
  365.                                     pWE: WEPtr);
  366.         var
  367.             pOrigin: LongIntPtr;
  368.             nLines: LongInt;
  369.     begin
  370.         pOrigin := @pWE^.hLines^^[lineIndex].lineOrigin;
  371.  
  372. { loop through the line run array adjusting the lineOrigin fields }
  373.         nLines := pWE^.nLines;
  374.         while (lineIndex <= nLines) do
  375.             begin
  376.                 pOrigin^ := pOrigin^ + deltaOrigin;
  377.                 pOrigin := LongIntPtr(LongInt(pOrigin) + SizeOf(LineRec));
  378.                 lineIndex := lineIndex + 1;
  379.             end;
  380.     end;  { _WEBumpOrigin }
  381.  
  382.     function _WEFindLineBreak (lineStart: LongInt;
  383.                                     hWE: WEHandle): LongInt;
  384.  
  385. { Find where to break the line beginning at lineStart }
  386. { the WE record and the text must be already locked }
  387. { the current graphics port must be already set up correctly }
  388.  
  389.         var
  390.             pWE: WEPtr;
  391.             pText: Ptr;
  392.             offset, breakOffset: LongInt;
  393.             textLength: LongInt;
  394.             remainingLength: LongInt;
  395.             segmentStart, segmentEnd: LongInt;
  396.             runIndex: LongInt;
  397.             runInfo: WERunInfo;
  398.             pixelWidth: Fixed;
  399.             script, previousScript: ScriptCode;
  400.             isBreak: Boolean;
  401.     begin
  402.         pWE := hWE^;
  403.         offset := lineStart;
  404.         pText := Ptr(LongInt(pWE^.hText^) + offset);
  405.         remainingLength := pWE^.textLength - offset;
  406.  
  407. { find the style run index corresponding to the first segment on this line }
  408.         runIndex := _WEOffsetToRun(offset, hWE);
  409.  
  410. { initialize pixelWidth to the width of the destination rectangle, as a Fixed quantity }
  411.         pixelWidth := BSL(pWE^.destRect.right - pWE^.destRect.left, 16);
  412.  
  413. { STYLE SEGMENT LOOP }
  414.         repeat
  415.  
  416. { get style run information for the current style run }
  417.             _WEGetIndStyle(runIndex, runInfo, hWE);
  418.             runIndex := runIndex + 1;
  419.  
  420. { set text attributes in the graphics port }
  421.             TextFont(runInfo.runAttrs.runStyle.tsFont);
  422.             %_TextFace(runInfo.runAttrs.runStyle.tsFace);
  423.             TextSize(runInfo.runAttrs.runStyle.tsSize);
  424.  
  425. { if we're handling multiscript text, keep track of script boundaries }
  426.             if BTST(pWE^.flags, weFNonRoman) then
  427.                 begin
  428.  
  429. { what is the script for this segment? }
  430.                     script := Font2Script(runInfo.runAttrs.runStyle.tsFont);
  431.  
  432. { have we crossed a script run boundary in the middle of a line? }
  433.                     if (runInfo.runStart > offset) and (script <> previousScript) then
  434.                         begin
  435.  
  436. { leave behind the all previous segments on this line }
  437.                             offset := runInfo.runStart;
  438.                             pText := Ptr(LongInt(pWE^.hText^) + offset);
  439.                             remainingLength := pWE^.textLength - offset;
  440.                         end;
  441.  
  442.                     previousScript := script;
  443.                 end;  { if non-Roman }
  444.  
  445. { we'll pass textLength as the second parameter to StyledLineBreak }
  446. { although this parameter is declared as a long, StyledLineBreak uses only }
  447. { the low word, so make sure it doesn't trespass the 32,767 byte threshold! }
  448.             textLength := _WEPinInRange(remainingLength, 0, maxint);
  449.  
  450. { calculate segmentStart and segmentEnd relative to offset }
  451.             segmentStart := _WEPinInRange(runInfo.runStart - offset, 0, textLength);
  452.             segmentEnd := _WEPinInRange(runInfo.runEnd - offset, 0, textLength);
  453.  
  454. { set breakOffset to a non-zero value for the first script run on the line, }
  455. { set it to zero for all subsequent script runs }
  456.             breakOffset := Integer(offset = lineStart);
  457.  
  458.             if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  459.                 begin
  460.  
  461. { EMBEDDED OBJECT }
  462. { subtract object width from pixelWidth }
  463.                     pixelWidth := pixelWidth - BSL(WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.h, 16);
  464.  
  465. { stop looping if pixelWidth has gone negative }
  466.                     isBreak := (pixelWidth < 0);
  467.  
  468.                     if (isBreak) then
  469.                         breakOffset := segmentStart        { break line before the object }
  470.                     else
  471.                         breakOffset := segmentEnd;        { break line after the object }
  472.  
  473.                 end
  474.             else
  475.                 begin
  476.  
  477. { REGULAR TEXT }
  478. { StyledLineBreak does the work for us }
  479.                     isBreak := (StyledLineBreak(pText, textLength, segmentStart, segmentEnd, 0, pixelWidth, breakOffset) <> smBreakOverflow);
  480.  
  481.                 end;
  482.  
  483. { break the line anyway when we reach the end of the text }
  484.             if (segmentEnd >= remainingLength) then
  485.                 isBreak := true;
  486.  
  487.         until (isBreak);
  488.  
  489. { return the offset from lineStart to the break point }
  490.         _WEFindLineBreak := (offset - lineStart) + breakOffset;
  491.  
  492.     end;  { _WEFindLineBreak }
  493.  
  494.     procedure _WECalcHeights (rangeStart, rangeEnd: LongInt;
  495.                                     var lineAscent, lineDescent: Integer;
  496.                                     hWE: WEHandle);
  497.  
  498. { Find the maximum ascent and descent values between rangeStart and rangeEnd }
  499. { the WE record must be already locked }
  500. { the current graphics port must be already set up correctly }
  501.  
  502.         var
  503.             runIndex: LongInt;
  504.             runInfo: WERunInfo;
  505.             runAscent, runDescent: Integer;
  506.     begin
  507.         lineAscent := 1;
  508.         lineDescent := 1;
  509.  
  510. { find the style run index corresponding to the first segment on this line }
  511.         runIndex := _WEOffsetToRun(rangeStart, hWE);
  512.  
  513. { STYLE SEGMENT LOOP }
  514.         repeat
  515.  
  516. { get style run information for the current style run }
  517.             _WEGetIndStyle(runIndex, runInfo, hWE);
  518.             runIndex := runIndex + 1;
  519.  
  520. { calculate ascent and descent (actually, descent + leading) values for this style run }
  521.             if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  522.                 begin
  523.  
  524. { EMBEDDED OBJECT }
  525.                     runAscent := WEObjectDescHandle(runInfo.runAttrs.runStyle.tsObject)^^.objectSize.v;
  526.                     runDescent := 0;
  527.                 end
  528.             else
  529.                 begin
  530.  
  531. { REGULAR TEXT }
  532.                     runAscent := runInfo.runAttrs.runAscent;
  533.                     runDescent := runInfo.runAttrs.runHeight - runAscent;
  534.                 end;
  535.  
  536. { save the maximum values in lineAscent and lineDescent }
  537.             if (runAscent > lineAscent) then
  538.                 lineAscent := runAscent;
  539.             if (runDescent > lineDescent) then
  540.                 lineDescent := runDescent;
  541.  
  542. { keep looping until we reach rangeEnd }
  543.         until (runInfo.runEnd >= rangeEnd);
  544.  
  545.     end;  { _WECalcHeights }
  546.  
  547.     function _WERecalBreaks (var startLine, endLine: LongInt;
  548.                                     hWE: WEHandle): OSErr;
  549.  
  550. { Recalculates line breaks, line heights and ascents for all the text or for a portion of it. }
  551. { On entry, startLine and endLine define a range of lines to recalculate. }
  552. { On exit, startLine to endLine defines the range of lines actually recalculated }
  553. { the WE record must already be locked }
  554.  
  555.         label
  556.             1;
  557.         var
  558.             pWE: WEPtr;
  559.             pLine: LinePtr;
  560.             lineInfo, oldLineInfo: LineRec;
  561.             lineIndex: LongInt;
  562.             recalThreshold: LongInt;
  563.             lineOffset: LongInt;
  564.             lineAscent, lineDescent: Integer;
  565.             saveTextLock: Boolean;
  566.             saveEnvironment: QDEnvironment;
  567.             err: OSErr;
  568.     begin
  569.         _WERecalBreaks := noErr;
  570.         pWE := hWE^;
  571.  
  572. { lock the text }
  573.         saveTextLock := _WESetHandleLock(pWE^.hText, true);
  574.  
  575. { find the character offset that must be necessarily reached before we can }
  576. { even consider the possibility of stopping the recalculation process }
  577. { this offset, recalThreshold, is the last character on endLine _before_ recalculation }
  578.         lineIndex := _WEPinInRange(endLine, 0, pWE^.nLines - 1);
  579.         recalThreshold := pWE^.hLines^^[lineIndex + 1].lineStart;
  580.  
  581. { we start recalculating line breaks from the line actually _preceding_ startLine, }
  582. { since editing startLine may cause part of its text to fit on the preceding line }
  583.         lineIndex := _WEPinInRange(startLine - 1, 0, pWE^.nLines - 1);
  584.  
  585. { find where in the text recalculation should begin }
  586.         lineInfo := pWE^.hLines^^[lineIndex];
  587.  
  588. { save the QuickDraw environment }
  589.         _WESaveQDEnvironment(pWE^.port, false, saveEnvironment);
  590.  
  591. { MAIN LINE BREAKING LOOP }
  592.         repeat
  593.  
  594. { find where to break the current line }
  595.             lineOffset := _WEFindLineBreak(lineInfo.lineStart, hWE);
  596.  
  597. { make sure we advance at least by one character (unless we reached the end of text) }
  598.             if (lineOffset <= 0) then
  599.                 if (lineInfo.lineStart < pWE^.textLength) then
  600.                     lineOffset := 1;
  601.  
  602. { calculate ascent and descent values for this line }
  603.             _WECalcHeights(lineInfo.lineStart, lineInfo.lineStart + lineOffset, lineAscent, lineDescent, hWE);
  604.  
  605. { save the maximum line ascent for this line in the line array }
  606.             pLine := @pWE^.hLines^^[lineIndex];
  607.             pLine^.lineAscent := lineAscent;
  608.  
  609. { increment counters (go to the next line array entry) }
  610.             lineIndex := lineIndex + 1;
  611.             lineInfo.lineStart := lineInfo.lineStart + lineOffset;
  612.             lineInfo.lineOrigin := lineInfo.lineOrigin + (lineAscent + lineDescent);
  613.             pLine := LinePtr(LongInt(pLine) + SizeOf(LineRec));
  614.  
  615. { compare the newly calculated line start with the old value }
  616. { if the new line start comes before the old line start, insert a new element }
  617.             oldLineInfo := pLine^;
  618.             if (lineIndex > pWE^.nLines) | (lineInfo.lineStart < oldLineInfo.lineStart) then
  619.                 begin
  620.                     err := InsertLine(lineIndex, lineInfo, pWE);
  621.  
  622. { clean up and exit if we ran out of memory }
  623.                     if (err <> noErr) then
  624.                         begin
  625.                             _WERecalBreaks := err;
  626.                             goto 1;
  627.                         end;
  628.                 end
  629.             else
  630.                 begin
  631.  
  632. { overwrite the old element }
  633.                     pLine^.lineStart := lineInfo.lineStart;
  634.                     pLine^.lineOrigin := lineInfo.lineOrigin;
  635.  
  636. { remove all further elements which have a lineStart field }
  637. { less than or equal to the current one }
  638.                     while (lineIndex + 1 <= pWE^.nLines) & (lineInfo.lineStart >= pWE^.hLines^^[lineIndex + 1].lineStart) do
  639.                         err := _WERemoveLine(lineIndex + 1, pWE);
  640.  
  641. { if the new line start is the same as the old one... }
  642.                     if (lineInfo.lineStart = oldLineInfo.lineStart) then
  643.                         begin
  644.  
  645. { ...and recalThreshold has been reached, we can stop recalculating line breaks }
  646.                             if (lineInfo.lineStart >= recalThreshold) then
  647.                                 begin
  648. { although line breaks need not be changed from lineIndex on, }
  649. { the lineOrigin fields may need to be changed }
  650.                                     if (lineInfo.lineOrigin <> oldLineInfo.lineOrigin) then
  651.                                         _WEBumpOrigin(lineIndex + 1, lineInfo.lineOrigin - oldLineInfo.lineOrigin, pWE);
  652.  
  653. { exit from the line breaking loop }
  654.                                     goto 1;
  655.                                 end;
  656.                         end
  657.                     else
  658.                         begin
  659.  
  660. { otherwise, the new line start comes after the old line start... }
  661. { if the current line is the one preceding startLine, warn our caller about this }
  662.                             if ((lineIndex > 0) and (lineIndex = startLine)) then
  663.                                 startLine := lineIndex - 1;
  664.  
  665.                         end;
  666.                 end;
  667.  
  668.         until (lineInfo.lineStart >= pWE^.textLength);
  669.  
  670. 1:
  671. { set destRect.bottom to destRect.top + total text height }
  672.         pWE^.destRect.bottom := pWE^.destRect.top + WEGetHeight(0, pWE^.nLines, hWE);
  673.  
  674. { quirk: if the last character in the text is a carriage return, the caret appears }
  675. { below the last line, so in this case we need to add the extra height to destRect.bottom }
  676.         if (WEGetChar(pWE^.textLength - 1, hWE) = CHR(13)) then
  677.             pWE^.destRect.bottom := pWE^.destRect.bottom + WEGetHeight(pWE^.nLines - 1, pWE^.nLines, hWE);
  678.  
  679. { return through endLine the index of the last line affected by recalculation }
  680.         endLine := lineIndex - 1;
  681.  
  682. { make sure startLine isn't greater than endLine }
  683.         if (startLine > endLine) then
  684.             startLine := endLine;
  685.  
  686. { unlock the text }
  687.         IgnoreBoolean(_WESetHandleLock(pWE^.hText, saveTextLock));
  688.  
  689. { restore the QuickDraw environment }
  690.         _WERestoreQDEnvironment(saveEnvironment);
  691.  
  692.     end;  { _WERecalBreaks }
  693.  
  694.     procedure _WERecalSlops (firstLine, lastLine: LongInt;
  695.                                     hWE: WEHandle);
  696.  
  697. { Calculates the lineSlop and lineJustAmount fields }
  698. { of the line array for the specified lines }
  699.  
  700.         var
  701.             pWE: WEPtr;
  702.             textLength: LongInt;
  703.             totalSlop, lineWidth: Integer;
  704.             totalProportion: Fixed;
  705.  
  706.         function SLCalcSlop (pLine: LinePtr;
  707.                                         pAttrs: WERunAttributesPtr;
  708.                                         pSegment: Ptr;
  709.                                         segmentStart, segmentLength: LongInt;
  710.                                         styleRunPosition: JustStyleCode): Boolean;
  711.             var
  712.                 segmentWidth: Integer;
  713.                 segmentProportion: Fixed;
  714.                 isEndOfLine: Boolean;
  715.         begin
  716.             SLCalcSlop := false;                { keep looping }
  717.  
  718. { see if this text segment ends with a carriage return, or if we've reached the }
  719. { end of the text (in which case we don't want any justification to take place) }
  720.             isEndOfLine := (segmentStart + segmentLength >= textLength) | (Ptr(LongInt(pSegment) + segmentLength - 1)^ = kEOL);
  721.  
  722. { if this is the first segment on the line, reset line totals }
  723.             if (styleRunPosition <= smLeftStyleRun) then
  724.                 begin
  725.                     totalSlop := lineWidth;
  726.                     totalProportion := 0;
  727.                 end;
  728.  
  729.             if (pAttrs^.runStyle.tsObject <> kNullObject) then
  730.                 begin
  731.  
  732. { EMBEDDED OBJECT }
  733. { segment width is just object width; no extra space can be applied for justification }
  734.                     segmentWidth := WEObjectDescHandle(pAttrs^.runStyle.tsObject)^^.objectSize.h;
  735.                     segmentProportion := 0;
  736.                 end
  737.             else
  738.                 begin
  739.  
  740. { REGULAR TEXT }
  741. { if this is the last segment on the line, strip trailing spaces }
  742.                     if (not Odd(styleRunPosition)) then
  743.                         segmentLength := VisibleLength(pSegment, segmentLength);
  744.  
  745. { measure this segment }
  746.                     segmentWidth := TextWidth(pSegment, 0, segmentLength);
  747.  
  748. { calculate the proportion of extra space to apply to this text segment }
  749.                     segmentProportion := NPortionText(pSegment, segmentLength, styleRunPosition, Point(kOneToOneScaling), Point(kOneToOneScaling));
  750.                 end;
  751.  
  752. { keep track of line totals }
  753.             totalSlop := totalSlop - segmentWidth;
  754.             totalProportion := totalProportion + segmentProportion;
  755.  
  756. { if this is the last segment on the line, save values in the line array }
  757.             if (not Odd(styleRunPosition)) then
  758.                 begin
  759.  
  760. { make sure slop is non-negative }
  761.                     if (totalSlop < 0) then
  762.                         totalSlop := 0;
  763.                     pLine^.lineSlop := totalSlop;
  764.  
  765.                     if (isEndOfLine) then
  766.                         pLine^.lineJustAmount := 0
  767.                     else
  768.                         pLine^.lineJustAmount := FixDiv(BSL(totalSlop, 16), totalProportion);
  769.  
  770.                 end;
  771.         end;  { SLCalcSlop }
  772.  
  773.     begin
  774.         pWE := hWE^;
  775.         textLength := pWE^.textLength;
  776.         lineWidth := pWE^.destRect.right - pWE^.destRect.left;
  777.  
  778. { calculate slop and normalized slop proportion for all lines }
  779.         _WESegmentLoop(firstLine, lastLine, SLCalcSlop, hWE);
  780.  
  781.     end;  { _WERecalSlops }
  782.  
  783.     function WECalText (hWE: WEHandle): OSErr;
  784.         var
  785.             startLine, endLine: LongInt;
  786.             saveWELock: Boolean;
  787.             err: OSErr;
  788.     begin
  789.  
  790. { lock WE record }
  791.         saveWELock := _WESetHandleLock(hWE, true);
  792.  
  793. { recalculate all line breaks }
  794.         startLine := 0;
  795.         endLine := maxLongInt;
  796.         err := _WERecalBreaks(startLine, endLine, hWE);
  797.  
  798. { recalculate line slops }
  799.         if (err = noErr) then
  800.             _WERecalSlops(startLine, endLine, hWE);
  801.  
  802. { unlock the WE record }
  803.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  804.  
  805. { return result code }
  806.         WECalText := err;
  807.  
  808.     end;  { WECalText }
  809.  
  810.     function WEUseText (text: Handle;
  811.                                     hWE: WEHandle): OSErr;
  812.         var
  813.             pWE: WEPtr;
  814.             textLength: LongInt;
  815.             saveWELock: Boolean;
  816.     begin
  817.         WEUseText := noErr;
  818.  
  819. { lock the WE record }
  820.         saveWELock := _WESetHandleLock(hWE, true);
  821.         pWE := hWE^;
  822.  
  823. { install the text }
  824.         _WEForgetHandle(pWE^.hText);
  825.         pWE^.hText := text;
  826.         textLength := %_GetHandleSize(text);
  827.         pWE^.textLength := textLength;
  828.         pWE^.hRuns^^[pWE^.nRuns].runStart := textLength + 1;
  829.         pWE^.hLines^^[pWE^.nLines].lineStart := textLength;
  830.  
  831. { unlock the WE record }
  832.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  833.  
  834.     end;  { WEUseText }
  835.  
  836.     function WEGetAlignment (hWE: WEHandle): SignedByte;
  837.     begin
  838.         WEGetAlignment := hWE^^.alignment;
  839.     end;  { WEGetAlignment }
  840.  
  841.     procedure WEGetSelection (var selStart, selEnd: LongInt;
  842.                                     hWE: WEHandle);
  843.         var
  844.             pWE: WEPtr;
  845.     begin
  846.         pWE := hWE^;
  847.         selStart := pWE^.selStart;
  848.         selEnd := pWE^.selEnd;
  849.     end;  { WEGetSelection }
  850.  
  851.     procedure WESetDestRect (var destRect: LongRect;
  852.                                     hWE: WEHandle);
  853.     begin
  854.         hWE^^.destRect := destRect;
  855.     end;  { WESetDestRect }
  856.  
  857.     procedure WEGetDestRect (var destRect: LongRect;
  858.                                     hWE: WEHandle);
  859.     begin
  860.         destRect := hWE^^.destRect;
  861.     end;  { WEGetDestRect }
  862.  
  863.     procedure WESetViewRect (var viewRect: LongRect;
  864.                                     hWE: WEHandle);
  865.         var
  866.             pWE: WEPtr;
  867.             r: Rect;
  868.     begin
  869.         pWE := hWE^;
  870.         pWE^.viewRect := viewRect;
  871.  
  872. { keep the viewRgn in sync with the view rectangle }
  873.         WELongRectToRect(viewRect, r);
  874.         RectRgn(pWE^.viewRgn, r);
  875.  
  876.     end;  { WESetViewRect }
  877.  
  878.     procedure WEGetViewRect (var viewRect: LongRect;
  879.                                     hWE: WEHandle);
  880.     begin
  881.         viewRect := hWE^^.viewRect;
  882.     end;  { WEGetViewRect }
  883.  
  884.     function WEGetTextLength (hWE: WEHandle): LongInt;
  885.     begin
  886.         WEGetTextLength := hWE^^.textLength;
  887.     end;  { WEGetTextLength }
  888.  
  889.     function WECountLines (hWE: WEHandle): LongInt;
  890.     begin
  891.         WECountLines := hWE^^.nLines;
  892.     end;  { WECountLines }
  893.  
  894.     function WEGetHeight (startLine, endLine: LongInt;
  895.                                     hWE: WEHandle): LongInt;
  896.         var
  897.             pWE: WEPtr;
  898.             pLines: LineArrayPtr;
  899.             nLines: LongInt;
  900.     begin
  901.         pWE := hWE^;
  902.         pLines := pWE^.hLines^;
  903.         nLines := pWE^.nLines;
  904.         startLine := _WEPinInRange(startLine, 0, nLines);
  905.         endLine := _WEPinInRange(endLine, 0, nLines);
  906.         _WEReorder(startLine, endLine);
  907.         WEGetHeight := pLines^[endLine].lineOrigin - pLines^[startLine].lineOrigin;
  908.     end;  { WEGetHeight }
  909.  
  910.     function WEGetText (hWE: WEHandle): Handle;
  911.     begin
  912.         WEGetText := hWE^^.hText;
  913.     end;  { WEGetText }
  914.  
  915.     function WEGetChar (offset: LongInt;
  916.                                     hWE: WEHandle): Char;
  917.         var
  918.             pWE: WEPtr;
  919.     begin
  920.         WEGetChar := Char(0);
  921.         pWE := hWE^;
  922.  
  923. { sanity check: make sure offset is withing allowed bounds }
  924.         if ((offset < 0) or (offset >= pWE^.textLength)) then
  925.             Exit(WEGetChar);
  926.  
  927. { get the specified character (actually, byte) }
  928.         WEGetChar := WECharsHandle(pWE^.hText)^^[offset];
  929.  
  930.     end;  { WEGetChar }
  931.  
  932.     function WEFeatureFlag (feature: Integer;
  933.                                     action: Integer;
  934.                                     hWE: WEHandle): Integer;
  935.         var
  936.             pWE: WEPtr;
  937.     begin
  938.         pWE := hWE^;
  939.  
  940. { return current status }
  941.         WEFeatureFlag := Integer(BTST(pWE^.flags, feature));
  942.  
  943. { set new status if necessary }
  944.         if (action = weBitClear) then
  945.             BCLR(pWE^.flags, feature)
  946.         else if (action = weBitSet) then
  947.             BSET(pWE^.flags, feature);
  948.  
  949.     end;  { WEFeatureFlag }
  950.  
  951.     function WEGetInfo (selector: OSType;
  952.                                     info: Ptr;
  953.                                     hWE: WEHandle): OSErr;
  954.     begin
  955.         WEGetInfo := _WEGetField(_WEMainSelectorTable, selector, info, hWE^);
  956.     end;  { WEGetInfo }
  957.  
  958.     function WESetInfo (selector: OSType;
  959.                                     info: Ptr;
  960.                                     hWE: WEHandle): OSErr;
  961.     begin
  962.         WESetInfo := _WESetField(_WEMainSelectorTable, selector, info, hWE^);
  963.     end;  { WESetInfo }
  964.  
  965. end.